#include <cassert>
#include <fstream>
#include <memory>
#include <unistd.h>
#include "Util.hpp"

extern mylog::Util::JsonData* g_conf_data;
namespace mylog{
    class LogFlush // 抽象基类，定义日志刷盘接口 Flush
    {
    public:
        using ptr = std::shared_ptr<LogFlush>;
        virtual ~LogFlush() {}
        virtual void Flush(const char *data, size_t len) = 0;//不同的写文件方式Flush的实现不同
    };

    class StdoutFlush : public LogFlush // 直接调用 std::cout 输出日志；Flush 方法里不涉及文件操作或同步，线程安全依赖外部控制（如 AsyncWorker）
    {
    public:
        using ptr = std::shared_ptr<StdoutFlush>;
        void Flush(const char *data, size_t len) override{
            cout.write(data, len);
        }
    };

    class FileFlush : public LogFlush // 这是最基础的文件刷盘方式，没有滚动机制，适合日志量较小的场景
    {
    public:
        using ptr = std::shared_ptr<FileFlush>;
        FileFlush(const std::string &filename) : filename_(filename) // 打开文件（追加模式 ab），如果失败打印错误
        {
            // 创建所给目录
            Util::File::CreateDirectory(Util::File::Path(filename));
            // 打开文件
            fs_ = fopen(filename.c_str(), "ab");
            if(fs_==NULL){
                std::cout <<__FILE__<<__LINE__<<"open log file failed"<< std::endl;
                perror(NULL);
            }
        }
        void Flush(const char *data, size_t len) override{ // 根据配置 g_conf_data->flush_log 决定刷盘策略：
            fwrite(data,1,len,fs_);
            if(ferror(fs_)){
                std::cout <<__FILE__<<__LINE__<<"write log file failed"<< std::endl;
                perror(NULL);
            }
            if(g_conf_data->flush_log == 1){
                if(fflush(fs_)==EOF){
                    std::cout <<__FILE__<<__LINE__<<"fflush file failed"<< std::endl;
                    perror(NULL);
                }
            }else if(g_conf_data->flush_log == 2){
                fflush(fs_);
                fsync(fileno(fs_));
            }
        }

    private:
        std::string filename_;
        FILE* fs_ = NULL; 
    };

    class RollFileFlush : public LogFlush // 滚动日志文件
    {
    public:
        using ptr = std::shared_ptr<RollFileFlush>;
        RollFileFlush(const std::string &filename, size_t max_size)
            : max_size_(max_size), basename_(filename)
        {
            Util::File::CreateDirectory(Util::File::Path(filename));
        }

        void Flush(const char *data, size_t len) override
        {
            // 确认文件大小不满足滚动需求
            InitLogFile();
            // 向文件写入内容
            fwrite(data, 1, len, fs_);
            if(ferror(fs_)){
                std::cout <<__FILE__<<__LINE__<<"write log file failed"<< std::endl;
                perror(NULL);
            }
            cur_size_ += len;
            if(g_conf_data->flush_log == 1){
                if(fflush(fs_)){
                    std::cout <<__FILE__<<__LINE__<<"fflush file failed"<< std::endl;
                    perror(NULL);
                }
            }else if(g_conf_data->flush_log == 2){
                fflush(fs_);
                fsync(fileno(fs_));
            }
        }

    private:
        void InitLogFile() // 如果文件不存在或者大小超过 max_size_，重新创建文件；文件名通过 CreateFilename() 生成，带时间戳和序号，保证唯一
        {
            if (fs_==NULL || cur_size_ >= max_size_)
            {
                if(fs_!=NULL){
                    fclose(fs_);
                    fs_=NULL;
                }   
                std::string filename = CreateFilename();
                fs_=fopen(filename.c_str(), "ab");
                if(fs_==NULL){
                    std::cout <<__FILE__<<__LINE__<<"open file failed"<< std::endl;
                    perror(NULL);
                }
                cur_size_ = 0;
            }
        }

        // 构建落地的滚动日志文件名称
        std::string CreateFilename()
        {
            time_t time_ = Util::Date::Now();
            struct tm t;
            localtime_r(&time_, &t);
            std::string filename = basename_;
            filename += std::to_string(t.tm_year + 1900);
            filename += std::to_string(t.tm_mon + 1);
            filename += std::to_string(t.tm_mday);
            filename += std::to_string(t.tm_hour + 1);
            filename += std::to_string(t.tm_min + 1);
            filename += std::to_string(t.tm_sec + 1) + '-' +
                        std::to_string(cnt_++) + ".log";
            return filename;
        }

    private:
        size_t cnt_ = 1;
        size_t cur_size_ = 0;
        size_t max_size_;
        std::string basename_;
        // std::ofstream ofs_;
        FILE* fs_ = NULL;
    };

    class LogFlushFactory
    {
    public:
        using ptr = std::shared_ptr<LogFlushFactory>;
        template <typename FlushType, typename... Args> // 这是一个 模板方法，支持创建不同类型的日志落地类；typename... Args 是 可变参数模板，表示可以传任意数量的参数
        static std::shared_ptr<LogFlush> CreateLog(Args &&...args) // 静态方法 → 不需要创建 LogFlushFactory 对象，就能直接调用；返回值是 std::shared_ptr<LogFlush>，即一个指向基类 LogFlush 的智能指针；这样做的好处是：调用者拿到的是基类指针，可以统一处理不同的日志写入方式。
        {
            return std::make_shared<FlushType>(std::forward<Args>(args)...); // 核心代码：创建一个 FlushType 类型的对象，并返回一个智能指针。
        }
    };
} // namespace mylog

// 工厂模式

// 目的：把对象的创建逻辑抽离出来，交给一个工厂类去负责。调用者只管“要什么对象”，而不管“怎么创建”。

// 典型场景：你需要根据条件动态生成不同的对象。

// 核心：封装创建对象的细节。

// LogFlushFactory 是一个静态工厂类。

// 你告诉它“我要什么类型的日志写入器”，它就用 make_shared 帮你创建好。

// 返回的是基类 LogFlush 的智能指针，调用者可以统一用 Flush() 接口，而不用关心具体实现。